﻿#ifndef SH_ONLY_HRADER
#include "buffer.hpp"
#endif
#include <cmath>
#include <algorithm>

#ifdef min
#undef min
#endif

#ifdef max
#undef max
#endif


namespace sh {

namespace detail {
static bool _compare_buffer(Buffer::const_iterator l_first, Buffer::const_iterator l_last, Buffer::const_iterator r_first, Buffer::const_iterator r_last) {
	if(l_last - l_first != r_last - r_first) return false;
	while (l_first != l_last) {
		if(*l_first != *r_first) return false;
		l_first++;
		r_first++;
	}
	return true;
}
} // namesapce detail

SH_ONLY_HRADER_INLINE 
Buffer::Buffer() noexcept
{
	
}

SH_ONLY_HRADER_INLINE 
Buffer::Buffer(const value_type* data, size_type n)
	: buffer_{data, data+n}
{
	
}

SH_ONLY_HRADER_INLINE 
Buffer::Buffer(size_type n, value_type elem)
	: buffer_(n, elem)
{
	
}

//SH_ONLY_HRADER_INLINE 
//Buffer::Buffer(const_iterator first, const_iterator last)
//	: buffer_(first, last)
//{
//}

SH_ONLY_HRADER_INLINE 
Buffer::Buffer(std::initializer_list<value_type> list)
	: buffer_(list)
{

}

SH_ONLY_HRADER_INLINE 
Buffer::Buffer(const Buffer& rhs)
	: buffer_{ rhs.buffer_}
{
	
}

SH_ONLY_HRADER_INLINE 
Buffer::Buffer(Buffer&& rhs) noexcept
{
	buffer_.swap(rhs.buffer_);
}

SH_ONLY_HRADER_INLINE 
Buffer& Buffer::operator=(Buffer const& rhs)
{
	buffer_ = rhs.buffer_;
	return *this;
}

SH_ONLY_HRADER_INLINE 
Buffer& Buffer::operator=(Buffer&& rhs) noexcept
{
	buffer_.swap(rhs.buffer_);
	return *this;
}

SH_ONLY_HRADER_INLINE
Buffer& Buffer::operator=(std::initializer_list<value_type> list)
{
	buffer_ = list;
	return *this;
}

SH_ONLY_HRADER_INLINE 
Buffer::~Buffer()
{

}





















SH_ONLY_HRADER_INLINE 
void Buffer::swap(Buffer& rhs)
{
	buffer_.swap(rhs.buffer_);
}

SH_ONLY_HRADER_INLINE 
void Buffer::fill(value_type ch)
{
	std::fill(buffer_.begin(), buffer_.end(), ch);
}

SH_ONLY_HRADER_INLINE
void Buffer::replace(size_type pos, size_type len, Buffer const& buffer)
{
	if(!len || pos + len > size()) return;
	const auto size = buffer.size();
	if (len > size) {
		size_t offset = len - size;
		remove(pos+offset, len-offset);
		len = size;
	}
	memcpy(data()+pos, buffer.data(), len);
	if(len < size) append(buffer.right(size-len));
}

SH_ONLY_HRADER_INLINE
void Buffer::replace(Buffer const& src, Buffer const& dst)
{
	if(src.empty() || src == dst) return;
	do {
		auto index = indexOf(src);
		if(index == npos) break;
		replace(index, src.size(), dst);
	}while(true);
}

SH_ONLY_HRADER_INLINE
void Buffer::insert(size_type index, value_type ch)
{
	if (index > size()) index = size();
	buffer_.insert(buffer_.begin() + index, ch);
}

SH_ONLY_HRADER_INLINE
void Buffer::insert(size_type index, Buffer const& buffer)
{
	if (buffer.empty()) return;
	if (index > size()) index = size();
	auto& inBuffer = buffer.buffer_;
	buffer_.insert(buffer_.begin() + index, inBuffer.begin(), inBuffer.end());
}

SH_ONLY_HRADER_INLINE
void Buffer::remove(size_type index)
{
	if (buffer_.size() <= index) return;
	buffer_.erase(buffer_.begin() + index);
}

SH_ONLY_HRADER_INLINE
void Buffer::remove(size_type index, size_type n)
{
	if (buffer_.size() <= index) return;
	if (buffer_.size() < index + n) n = buffer_.size() - index;
	buffer_.erase(buffer_.begin() + index, buffer_.begin() + index + n);
}

SH_ONLY_HRADER_INLINE
void Buffer::chop(size_type n)
{
	if (!n) return;
	n = std::min(n, buffer_.size());
	buffer_.erase(buffer_.end() - n, buffer_.end());
}

SH_ONLY_HRADER_INLINE
void Buffer::truncate(size_type n)
{
	if (!n || n >= buffer_.size()) return;
	buffer_.erase(buffer_.begin() + n, buffer_.end());
}

SH_ONLY_HRADER_INLINE
void Buffer::pop_front()
{
	if (!empty()) remove(0);
}

SH_ONLY_HRADER_INLINE 
void Buffer::append(value_type ch)
{
	buffer_.push_back(ch);
}

SH_ONLY_HRADER_INLINE 
void Buffer::append(Buffer const& buffer)
{
	if (!buffer.size()) return;
	auto& inBuffer = buffer.buffer_;
	buffer_.insert(buffer_.end(), inBuffer.begin(), inBuffer.end());
}

SH_ONLY_HRADER_INLINE 
void Buffer::push_front(value_type ch)
{
	insert(0, ch);
}

SH_ONLY_HRADER_INLINE 
void Buffer::push_front(Buffer const& buffer)
{
	insert(0, buffer);
}

SH_ONLY_HRADER_INLINE
void Buffer::prepend(value_type ch)
{
	insert(0, ch);
}

SH_ONLY_HRADER_INLINE
void Buffer::prepend(Buffer const& buffer)
{
	insert(0, buffer);
}

SH_ONLY_HRADER_INLINE 
Buffer Buffer::mid(size_type index, size_type n) const
{
	if (!n || index + n > size()) return Buffer();
	return Buffer(buffer_.data() + index, n);
}

SH_ONLY_HRADER_INLINE 
Buffer Buffer::left(size_type n) const
{
	if(buffer_.size() < n) return *this;
	return Buffer(buffer_.data(), n);
}

SH_ONLY_HRADER_INLINE 
Buffer Buffer::right(size_type n) const
{
	if (buffer_.size() < n) return *this;
	return Buffer(buffer_.data()+n, buffer_.size()-n);
}

SH_ONLY_HRADER_INLINE 
std::vector<Buffer> Buffer::split(value_type sep) const
{
	if(empty()) return {*this};
	std::vector<Buffer> list;
	const_iterator l = buffer_.begin();
	const_iterator r = l;
	while (r != buffer_.end()) {
		r = std::find(l, buffer_.end(), sep);
		list.emplace_back(Buffer(l, r));
		l = r;
	}
	return list;
}

SH_ONLY_HRADER_INLINE 
bool Buffer::contains(Buffer const& buffer) const
{
	//const auto& inBuffer = buffer.buffer_;
	//return std::search(buffer_.begin(), buffer_.end(), inBuffer.cbegin(), inBuffer.cend()) == cend();
	return indexOf(buffer) != npos;
}

SH_ONLY_HRADER_INLINE 
bool Buffer::startsWith(Buffer const& buffer) const
{
	if(size() < buffer.size()) return false;
	auto it = cbegin();
	for (auto const& value : buffer) 
		if(value != *(it++)) return false;
	return true;
}

SH_ONLY_HRADER_INLINE 
bool Buffer::endsWith(Buffer const& buffer) const
{
	if (size() < buffer.size()) return false;
	auto it = cbegin() + size() - buffer.size();
	for (auto const& value : buffer)
		if (value != *(it++)) return false;
	return true;
}

SH_ONLY_HRADER_INLINE 
Buffer::size_type Buffer::count(value_type ch) const
{
	size_type n = 0;
	for (auto const& val : buffer_) if(val == ch) ++n;
	return n;
}

SH_ONLY_HRADER_INLINE 
Buffer::size_type Buffer::count(Buffer const& buffer) const
{
	if(empty() || buffer.empty()) return 0;
	size_type n = 0, from = 0;
	while (true) {
		from = indexOf(buffer, from);
		if(from == npos) break;
		++from;
		++n;
	}
	return n;
}

SH_ONLY_HRADER_INLINE 
Buffer::size_type Buffer::indexOf(value_type ch, size_type from) const
{
	for (size_type i = from; i < buffer_.size(); i++) {
		if(ch == buffer_.at(i)) return i;
	}
	return npos;
}

SH_ONLY_HRADER_INLINE 
Buffer::size_type Buffer::indexOf(Buffer const& buffer, size_type from) const
{
	if(from > buffer_.size() || buffer.empty()) return npos;

	size_type n = buffer.size();

	for (auto it = buffer_.cbegin() + from; it < buffer_.cend() - n; ++it) {
		if(detail::_compare_buffer(it, it + n, buffer.cbegin(), buffer.cend())) return it - buffer_.cbegin();
	}

	return npos;
}






SH_ONLY_HRADER_INLINE 
std::string Buffer::toHex() const
{
	static const char* HexStr = "0123456789ABCDEF";
	std::string str;
	for (auto const& ch : buffer_) {
        str += HexStr[(uint8_t)ch / 16];
        str += HexStr[(uint8_t)ch % 16];
	}
	return str;
}

inline std::string Buffer::toHex(char separator) const
{
    auto str = toHex();
    if(str.empty() || separator == '\0') return str;
    for(auto it = str.begin(); it != str.end(); ) {
        it++; it++;
        if(it == str.end()) break;
        it = str.insert(it, separator);
        it++;
    }
    return str;
}

SH_ONLY_HRADER_INLINE 
Buffer Buffer::fromHex(std::string const& str)
{
	static auto fnGetIndex = [] (char ch) -> int {
		static const char* HexStr = "0123456789ABCDEF";
		for (int i = 0; i < 16; i++) {
			if (HexStr[i] == std::toupper(ch)) return i;
		}
		return -1;
	};

	if (str.empty()) return {};

	size_t i{};
	int index{};
	Buffer buffer;
	if (str.size() % 2) {
		++i;
		index = fnGetIndex(str.at(0));
		if (index < 0) return {};
		buffer.append(index);
	}

	for (; i < str.size(); ) {
		int high = fnGetIndex(str.at(i++));
		int low = fnGetIndex(str.at(i++));
		if (high < 0 || low < 0) return {};
		buffer.append(high << 4 | low);
	}

	return buffer;
}






SH_ONLY_HRADER_INLINE 
Buffer& Buffer::operator += (Buffer const& rhs) {
	append(rhs);
	return *this;
}

SH_ONLY_HRADER_INLINE 
Buffer Buffer::operator + (Buffer const& rhs)
{
	Buffer buffer = *this;
	buffer.append(rhs);
	return buffer;
}

SH_ONLY_HRADER_INLINE 
bool Buffer::operator == (Buffer const& rhs) const
{
	return buffer_ == rhs.buffer_;
}

SH_ONLY_HRADER_INLINE 
bool Buffer::operator != (Buffer const& rhs) const
{
	return buffer_ != rhs.buffer_;
}

} // namespace sh
